
#include "H264AVCVideoIoLib.h"

#include "LargeFile.h"

#include <errno.h>

#if defined( MSYS_WIN32 )
# include <io.h>
# include <sys/stat.h>
# include <fcntl.h>
#else
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
// heiko.schwarz@hhi.fhg.de: support for BSD systems as proposed by Steffen Kamp [kamp@ient.rwth-aachen.de]
#define _FILE_OFFSET_BITS 64
#endif
# include <sys/ioctl.h>
# include <sys/types.h>
# include <fcntl.h>
# include <dlfcn.h>
# include <cerrno>
# include <unistd.h>
#endif
#include <iostream>
#include<string>
using namespace std;
//Additions done

#include "ExceptionHandler.h"
#include "DBHelper.h"
#include "CommonUtilities.h"
#include<iostream>
#include<stdlib.h>
#include <stddef.h>

#include "SocketClient.h"

char* CLOUD_SERVER_HOST = "localhost";
int CLOUD_SERVER_PORT = 54310;

char* EDGE_SERVER_HOST = "localhost";
char * EDGE_SERVER_PORT = "6001";

//constants for socket communication
const char* CMD_ACCEPT = "cmd_accept";
const char* ACK_ACCEPT = "ack_accept";
const char* CMD_READY = "cmd__ready";
const char* CMD_ERROR = "cmd__error";
const char* CMD_SUCCESS ="cmdsuccess";
const int CMD_LENGTH = 10;

using namespace utilities;
using namespace database;
using namespace logging;
using namespace sql;
using namespace networklib;


LargeFile::LargeFile() :
  m_iFileHandle( -1 )
{
	connObj=new HDConnection(CLOUD_SERVER_HOST,CLOUD_SERVER_PORT);
	connObj->Open();
	sockClient=NULL;
	//sockClient=new SocketClient(EDGE_SERVER_HOST,EDGE_SERVER_PORT);
	//sockClient=new SocketClient(EDGE_SERVER_HOST,EDGE_SERVER_PORT);
	m_hdfsFile=false;
	m_hdfsFileHandle=NULL;
}


LargeFile::~LargeFile()
{
  if( -1 != m_iFileHandle )
  {
    ::close( m_iFileHandle );
  }
  m_hdfsFile=false;
  delete connObj;
}

bool LargeFile::is_open()

{

		if(!m_hdfsFile)
		{
			if(m_iFileHandle!=-1)
		  return true;
			else
				return false;
		}
	  else
		  {
		  if(m_hdfsFileHandle!= NULL)
		 return true;
		  else
			  return false;
		  }
  }

ErrVal LargeFile::open( const std::string& rcFilename, enum OpenMode eOpenMode, int iPermMode )
{
  ROT( rcFilename.empty() );
  ROF( -1 == m_iFileHandle );


 // cout <<"Open Mode is :"<<eOpenMode<<endl;
  int iOpenMode;

#if defined( MSYS_WIN32 )

  if( eOpenMode == OM_READONLY )
  {
    iOpenMode = _O_RDONLY;
  }
  else if( eOpenMode == OM_WRITEONLY )
  {
    iOpenMode = _O_CREAT | _O_TRUNC | _O_WRONLY;
  }
  else if( eOpenMode == OM_APPEND )
  {
    //append mode does not imply write access and the create flag
    //simplifies the program's logic (in most cases).
    iOpenMode = _O_APPEND | _O_CREAT | _O_WRONLY;
  }
  else if( eOpenMode == OM_READWRITE )
  {
    iOpenMode = _O_CREAT | _O_RDWR;
  }
  else
  {
    AF();
    return Err::m_nERR;
  }

  iOpenMode |= _O_SEQUENTIAL | _O_BINARY;

  m_iFileHandle = ::open( rcFilename.c_str(), iOpenMode, iPermMode );

#elif defined( MSYS_UNIX_LARGEFILE )

  if( eOpenMode == OM_READONLY )
  {
    iOpenMode = O_RDONLY;
    //set the hdfs attribute to read file from HDFS
    m_hdfsFile=true;
  }
  else if( eOpenMode == OM_WRITEONLY )
  {
    iOpenMode = O_CREAT | O_TRUNC | O_WRONLY;

    //set the hdfs attribute to read file from HDFS and write data to edge server
    //through socket
       m_hdfsFile=true;

      // cerr<<"now entered write mode"<<endl;
       //Additions
       if(hostname!=NULL && portno!=NULL)
       {
    	   cerr<<"hostname is :"<<hostname<<endl;
    	   cerr<<"portno is :"<<portno<<endl;

    	   sockClient=new SocketClient(hostname,portno);
       }

       //sockClient=new SocketClient(EDGE_SERVER_HOST,EDGE_SERVER_PORT);

       if (!sockClient->connected)
       {
       	cerr<<"Failed to connect to edge server in LFOpen()"<<endl;
       	exit(-1);

       }
       int ibyteCount;
       char* socketRecvBuffer;
       cerr<<"trying to send CMD_ACCEPT"<<endl;

       	// send file send command CMD_ACCEPT
       ibyteCount = sockClient->sendData((char*)CMD_ACCEPT, CMD_LENGTH);
       cerr<<"CMD_ACCEPT sent"<<endl;
       if (ibyteCount != CMD_LENGTH)
       {
       		//cerr<<"Failed to send ACCEPT command to edge server LFOpen()"<<endl;
       		sockClient->disconnect();
       		return -4; // socket communication error
       }

       	//wait for ack ACK_ACCEPTED
       	socketRecvBuffer = (char*)malloc(CMD_LENGTH);
       cerr<<"trying to receive ACCEPT ACK from Edge server"<<endl;


       	ibyteCount = sockClient->receiveData(socketRecvBuffer, CMD_LENGTH);

       cerr<<" rECEIVED ACCEPT ACK from Edge server"<<endl;


       	if (ibyteCount != CMD_LENGTH)
       	{
       		cerr<<"Failed to receive ACCEPT ack from edge server LFOpen()"<<endl;
       		sockClient->disconnect();
       		return -4; // socket communication error
       	}

		//cerr<<"ACK_ACCEPTED - "<<socketRecvBuffer<<endl;

		CommonUtilities* cu = new CommonUtilities();
		//int VendorID=67;//Vendor ID is a constant need to be modified later

		cerr<<"Vendor ID is :"<<vendorID;
		string sfullFilePath;
		sfullFilePath.append(cu->to_string(vendorID));
		sfullFilePath.append(",");
		sfullFilePath.append(rcFilename.c_str());
		sfullFilePath.append(",");
		//sfullFilePath.append(cu->to_string(chunkIndex));
		//Chunk index is a constant need to be modified later
		//sfullFilePath.append("1");
		sfullFilePath.append(cu->to_string(layerID));

		//sprintf(fullFilePath, "%d,%s,%d",VendorID, hdfsfileName, chunkIndex);

		char* fullFilePath = (char*)sfullFilePath.c_str();

       	cerr<<"Trying to send FilePath :"<<fullFilePath<<endl;

		// send file name
		ibyteCount = sockClient->sendData(fullFilePath, strlen(fullFilePath));

       	cerr<<"FilePath :"<<fullFilePath<<"Sent successfully"<<endl;

		//TODO: if required, wait for ready ack ACK_READY
		socketRecvBuffer = (char*)malloc(CMD_LENGTH);

		//cerr<<"Waiting to receive ok from the edge server before sending data"<<endl;
		ibyteCount = sockClient->receiveData(socketRecvBuffer, CMD_LENGTH);

		if (ibyteCount != CMD_LENGTH)
		{
		//cerr<<"Failed to receive READY command from edge server in LFOpen()"<<endl;
		sockClient->disconnect();
		return -4; // socket communication error
		}

//		cerr<<"ACK_READY - "<<socketRecvBuffer<<endl;
	//	cerr<<"Received OK from the edge server, now we have to send data..."<<endl;

  }
  else if( eOpenMode == OM_APPEND )
  {
    iOpenMode = O_APPEND | O_CREAT | O_WRONLY;
  }
  else if( eOpenMode == OM_READWRITE )
  {
    iOpenMode = O_CREAT | O_RDWR;
  }
  else
  {
    AOT( 1 );
    return Err::m_nERR;
  }
  cout<<"File name being opened is :"<<rcFilename.c_str()<<endl;

  // heiko.schwarz@hhi.fhg.de: support for BSD systems as proposed by Steffen Kamp [kamp@ient.rwth-aachen.de]
  //m_iFileHandle = open64( rcFilename.c_str(), iOpenMode, iPermMode );
  if(!m_hdfsFile)
  {


  m_iFileHandle = ::open( rcFilename.c_str(), iOpenMode, iPermMode );

  // check if file is really open
   ROTS( -1 == m_iFileHandle );

   // and return
   return Err::m_nOK;
  }
  else
  {
	  if( eOpenMode == OM_READONLY )
	  {
		  //HDConnection *conObj=new HDConnection(CLOUD_SERVER_HOST, CLOUD_SERVER_PORT);
		  //  conObj->Open();
		  m_hdfsFileHandle = hdfsOpenFile(connObj->cloudFS, rcFilename.c_str(), O_RDONLY, 0, 0, 0);

		if (!m_hdfsFileHandle)
		{
			cerr<<"Failed to open "<<rcFilename.c_str()<<" for reading!"<<endl;
			exit(-1);
		}
		else
			return Err::m_nOK;
	  }
  }
#endif


}

ErrVal LargeFile::close()
{
  int iRetv;

  if(!m_hdfsFile)
  {
  ROTS( -1 == m_iFileHandle );

  iRetv = ::close( m_iFileHandle );

  m_iFileHandle = -1;
  return ( iRetv == 0 ) ? Err::m_nOK : Err::m_nERR;
  }
  //Else block returns OK for Hdfs attribute because hdfsclose() is used after read and write in
  //those appropriate methods
  else
	  return Err::m_nOK;
}


ErrVal LargeFile::seek( Int64 iOffset, int iOrigin )
{
  Int64 iNewOffset;

  //cout<<"Entered LArgeFile Seek method"<<endl;
  //cerr<<"Origin value is :"<<iOrigin<<endl;
#if defined( MSYS_WIN32 )
  iNewOffset = _lseeki64( m_iFileHandle, iOffset, iOrigin );
#elif defined( MSYS_UNIX_LARGEFILE )
  // heiko.schwarz@hhi.fhg.de: support for BSD systems as proposed by Steffen Kamp [kamp@ient.rwth-aachen.de]
  //iNewOffset = lseek64( m_iFileHandle, iOffset, iOrigin );
  if(!m_hdfsFile)
  {
	  //cerr<<"TRIG1";
	  ROT( -1 == m_iFileHandle );

	  iNewOffset = ::lseek( m_iFileHandle, iOffset, iOrigin );
	  return ( iNewOffset == -1 ) ? Err::m_nERR : Err::m_nOK;
  }
  else
  {
	  //cerr<<"TRIG2";
	  //cout<<"HDFS hanNDLE is :"<<m_hdfsFileHandle<<endl;
	 // Int64 seekPos=iOffset+iOrigin;//QQSeekPos is referred as offset from the origin
	  Int64 hdfsoffset=0;
	   if(iOrigin==1)
	  {

		  hdfsoffset= hdfsTell(connObj->cloudFS, m_hdfsFileHandle);

		  iOffset+=  hdfsoffset;
	  }
	  if(hdfsSeek(connObj->cloudFS, m_hdfsFileHandle, iOffset))
		  {  cerr<< "Failed to seek "<<" for reading!"<<endl;
		  		  			exit(-1);
		  }



	  //cout<<"TRig reached seek  position is :"<<endl;
  return  Err::m_nOK;
  }

#endif


}

Int64 LargeFile::tell()
{
  Int64 iOffset;
#if defined( MSYS_WIN32 )
  iOffset = _telli64( m_iFileHandle );
#elif defined( MSYS_UNIX_LARGEFILE )
  // heiko.schwarz@hhi.fhg.de: support for BSD systems as proposed by Steffen Kamp [kamp@ient.rwth-aachen.de]
  //iOffset = lseek64( m_iFileHandle, 0, SEEK_CUR );

  if(!m_hdfsFile)
   {
	  ROTR( -1 == m_iFileHandle, -1 );

	  iOffset = ::lseek( m_iFileHandle, 0, SEEK_CUR );
	  ROT( iOffset == -1 )
    // and return
	  return iOffset;
   }
  else
  {
	 iOffset=hdfsTell(connObj->cloudFS, m_hdfsFileHandle);
	 //QQ do we have to store the seek position to validate with the output of seek
	 ROT( iOffset == -1 )
	 // and return
	 return iOffset;
  }
#endif

}

ErrVal LargeFile::read( Void *pvBuffer, UInt32 uiCount, UInt32& ruiBytesRead )
{
  int iRetv;

  if(!m_hdfsFile)
  {
  ROT( -1 == m_iFileHandle );
  ROT( 0 == uiCount );

  ruiBytesRead = 0;

  iRetv = ::read( m_iFileHandle, pvBuffer, uiCount );
  }
  else
  {
	  //num_read_bytes = hdfsPread(cloudConn->cloudFS, m_hdfsFileHandle, currentPos,pvBuffer,num_read_bytes);
	  		ruiBytesRead = 0;
	  		iRetv= hdfsRead(connObj->cloudFS,m_hdfsFileHandle,pvBuffer,uiCount);
  }

  if( iRetv != (Int)uiCount )
  {
    //need to handle partial reads before hitting EOF

    //If the function tries to read at end of file, it returns 0.
    //If the handle is invalid, or the file is not open for reading,
    //or the file is locked, the function returns -1 and sets errno to EBADF.
    if( iRetv > 0 )
    {
      //partial reads are acceptable and return the standard success code. Anything
      //else must be implemented by the caller.
      ruiBytesRead = iRetv;
      return Err::m_nOK;
    }
    else if( iRetv == -1 )
    {
      return errno;
    }
    else if( iRetv == 0)
    {
      return Err::m_nEndOfFile;
    }
    else
    {
      AOF( ! "fix me, unexpected return code" );
      return Err::m_nERR;
    }
  }
  else
  {
    ruiBytesRead = uiCount;
  }

  ROF( iRetv == (Int)uiCount );

  return Err::m_nOK;
  }


ErrVal LargeFile::write( const Void *pvBuffer, UInt32 uiCount )
{
  int iRetv;
  //cerr<<"Enterd LF write method !!!"<<endl;

  //socketaddition
  if(!m_hdfsFile)
  {
  ROT( -1 == m_iFileHandle );
  ROT( 0 == uiCount );

  iRetv = ::write( m_iFileHandle, pvBuffer, uiCount );
  ROF( iRetv == (Int)uiCount );
  //cerr<<"Exit LF write  method!!!"<<endl;

  return Err::m_nOK;
  }
  else
  {
	  ROT( 0 == uiCount );
	  if (!sockClient->connected)
	   {
	     cerr<<"Failed to connect to edge server in write Method"<<endl;
	     exit(-1);
	   }
	  //send 1024(uiCount) bytes at a time over socket to the client
	  iRetv = sockClient->sendData((char *)pvBuffer, uiCount);
	  ROF( iRetv == (Int)uiCount );
	  return Err::m_nOK;

  }
}

void LargeFile::customDelayAck()
{


	//cerr<<"LFcustomDelayAck() method triggered"<<endl;
	int ibyteCount;
	char *socketRecvBuffer = (char*)malloc(CMD_LENGTH);
	ibyteCount = sockClient->receiveData(socketRecvBuffer, CMD_LENGTH);

	if (ibyteCount != CMD_LENGTH)
	{
		cerr<<"Failed to receive READY command for SUCCESS from edge server"<<endl;
		sockClient->disconnect();
		exit(-1);
	}

	// send SUCCESS message
	sockClient->sendData((char*)CMD_SUCCESS, CMD_LENGTH);

	// close server connection
	sockClient->disconnect();



}
